// SSLServer.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>


#ifdef WIN32
#include <Winsock2.h>         // For socket(), connect(), send(), and recv()  
#else  
#endif

#define DEV printf("File %s line %d\n",__FILE__,__LINE__);

#include <openssl/rsa.h>       
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define DEFAULT_KEY_FILE ".\\TESTCERT.pem"
#define DEFAULT_CERT_FILE ".\\TESTCERT.pem"
#define DEFAULT_SERVER_PORT 9090
#define DEFAULT_SERVER_IP_ADDR INADDR_ANY

#define DEFAULT_MAX_BACKLOG 5
int CreateTCPServerSock(unsigned int uiIP, int nPort,int nBackLog)
{
	int iRet  = -1;
	int iFD = -1;
	do
	{
#ifdef WIN32
		static int nFirstTime = 1;
		if (1 == nFirstTime) 
		{
			WORD wVersionRequested;
			WSADATA wsaData;

			wVersionRequested = MAKEWORD(2, 0); // Request WinSock v2.0
			if (WSAStartup(wVersionRequested, &wsaData) != 0)
			{  
				// Load WinSock DLL
				return iFD;
			}
			nFirstTime = 0;
		}
#endif
		iFD = socket (AF_INET, SOCK_STREAM, 0); 
		if (-1 == iFD)
		{
			break;
		}
		struct sockaddr_in sa_serv;  
		memset (&sa_serv, '\0', sizeof(sa_serv));
		sa_serv.sin_family      = AF_INET;
		sa_serv.sin_addr.s_addr = uiIP;
		sa_serv.sin_port        = htons (nPort);      

		iRet = bind(iFD, (struct sockaddr*) &sa_serv, sizeof (sa_serv));    
		if (-1 == iRet)
		{
			break;
		}		
		/* Receive TCP connection. */

		iRet = listen (iFD, nBackLog);
		if (-1 == iRet)
		{
			break;
		}
	} while(0);
	if (iRet == -1)
	{
		char acMsgBuf[128]="";
		strerror_s( acMsgBuf, sizeof(acMsgBuf), errno);
		printf("TCP Socket can not be opened.Reason %s \n", acMsgBuf);
		return iFD = -1;
	}	
	return iFD;
}

int TCPSSLHandShake(int iFd, SSL* psSsl)
{
	int iRet = -1;
	do
	{		
		if (NULL == psSsl)
		{
			break;
		}
		SSL_set_fd (psSsl, iFd);
		iRet = SSL_accept (psSsl);  
		if (-1 == iRet)
		{
			ERR_print_errors_fp(stderr);
			break;
		}
		printf ("SSL connection using cipher %s\n", SSL_get_cipher (psSsl));

	}while(0);
	return iRet;
}
void PrintHelp()
{
	printf("Format for inputs parameters:\n");
	printf("<KeyFile> <Certificate File> <Port for incoming connections> <Server IP>\n");

	//arg 0 => program
	//arg 1 => KeyFile
	//arg 2 => Certificate File
	//arg 3 => Port for incoming connections
	//arg 4 => Server IP

}

int InitSSL(SSL_CTX* psCtx,const char* pcKeyFile, const char* pcCertFile)
{
	int iRet = -1;
	do
	{  
		if (SSL_CTX_use_certificate_file(psCtx, pcCertFile, SSL_FILETYPE_PEM) <= 0) 
		{
			ERR_print_errors_fp(stderr);
			break;
		}
		if (SSL_CTX_use_PrivateKey_file(psCtx, pcKeyFile, SSL_FILETYPE_PEM) <= 0)
		{
			ERR_print_errors_fp(stderr);
			break;
		}

		if (!SSL_CTX_check_private_key(psCtx))
		{
			fprintf(stderr,"Private key does not match the certificate public key\n");
			break;
		}
		iRet = 0;		
	} while(0);
	return iRet;
}


void CloseSocket(int iFD) 
{
#ifdef WIN32
	::closesocket(iFD);
#else
	::close(sockDesc);
#endif  
}
int GetAuthenticationStatus(const char *pcRecvdBuf,int iRecvdBufLen, char *pcReplydBuf,int iReplyBufLen)
{

	int iRet = -1;
	char* token = NULL,*saveptr = NULL ;
	char *pCurPos = (char*) pcRecvdBuf ;
	const char delimter[] =":,\n\t ";	
	int j = 0;	 
	char tokens[4][32];
	strcpy_s(pcReplydBuf, iReplyBufLen, "NokOK");
	while(j<4)
	{
		token = strtok_s(pCurPos, delimter, &saveptr);
		if (token == NULL)        
		{			
			break;
		}		
		strcpy_s(tokens[j],sizeof(tokens[j]),token);

		++j;
		pCurPos = NULL;
	}	
	if (( 0 == _stricmp(tokens[0],"UserID")) &&
		( 0 == _stricmp(tokens[2],"Password"))
		)
	{
		// verify if userid and passworkd is ok by some look up 
		//harcoding here
		if (( 0 == _stricmp(tokens[1],"TEST")) &&
			( 0 == _stricmp(tokens[3],"TESTPASS"))
			)
		{
			strcpy_s(pcReplydBuf,iReplyBufLen, "Verified");
			iRet = 0;
		}
	}
	return iRet;
}
int main(int argc, char* argv[])
{	
	int iRet = EXIT_FAILURE;
	if ((argc>=2) &&
		( 0 == _stricmp(argv[1],"Help"))
		)
	{
		PrintHelp();
		return EXIT_SUCCESS;
	}
	
	const char* pcKeyFile = DEFAULT_KEY_FILE;
	const char* pcCertFile = DEFAULT_CERT_FILE;
	int nServerPort = DEFAULT_SERVER_PORT;
	unsigned int uiServerIP = DEFAULT_SERVER_IP_ADDR;

	if (argc>=2)
	{
		pcKeyFile = argv[1];
	}
	if (argc>=3)
	{
		pcCertFile = argv[2];
	}
	if (argc>=4)
	{		
		nServerPort = atoi(argv[3]);
	}
	if (argc>=5)
	{
		uiServerIP = inet_addr(argv[4]);
	}
	printf("Starting application %s with key file %s certificate file %s on port %d\n",argv[0], pcKeyFile, pcCertFile, nServerPort );

	/* Prepare TCP socket for receiving connections */
	int iListenFD = -1;
	int iClientFD = -1;
	SSL* psSsl = NULL;
	SSL_CTX* psCtx = NULL;
	do
	{
		SSL_load_error_strings();
		SSLeay_add_ssl_algorithms();
		const SSL_METHOD *meth = SSLv23_server_method();
		psCtx = SSL_CTX_new (meth);
		if (NULL == psCtx) 
		{
			ERR_print_errors_fp(stderr);
			break;
		}

		iRet = InitSSL(psCtx, pcKeyFile, pcCertFile);
		if (-1 == iRet)
		{
			break;
		}

		int iBacklog = DEFAULT_MAX_BACKLOG;
		iListenFD = CreateTCPServerSock(uiServerIP, nServerPort, iBacklog );
		if (-1 == iListenFD)
		{			
			break;
		}
		for (;;)
		{
			struct sockaddr_in sa_cli;
			memset(&sa_cli, 0, sizeof(sa_cli));
			int iClientLen =  sizeof(sa_cli);			
			iClientFD = -1;
			iClientFD = accept (iListenFD, (struct sockaddr*) &sa_cli, &iClientLen);

			if (-1 == iClientFD )
			{
				break;
			}

			printf ("Incoming connection from %s, port %d\n", inet_ntoa (sa_cli.sin_addr), sa_cli.sin_port);
			psSsl = SSL_new (psCtx);
			if (NULL == psSsl)
			{
				break;
			}

			iRet = TCPSSLHandShake(iClientFD,psSsl);
			if (-1 == iRet)
			{
				break;
			}

			char acBuff[1400] = "";
			iRet = SSL_read (psSsl, acBuff, sizeof(acBuff));
			if (-1 == iRet) 
			{
				ERR_print_errors_fp(stderr);
				break;
			}
			int iRecvDataLen = iRet;
			printf ("Received %s\n", acBuff);
			char acAns[16] = "";
			GetAuthenticationStatus(acBuff, iRecvDataLen, acAns, sizeof(acAns));
			
			iRet = SSL_write (psSsl, acAns, strlen(acAns));
			if (-1 == iRet)
			{
				ERR_print_errors_fp(stderr);
				break;
			}
			if (-1 != iClientFD)
			{
				CloseSocket(iClientFD);
			}
		}

		iRet = EXIT_SUCCESS;
	} while(0);

	if (-1 != iListenFD)
	{
		CloseSocket(iListenFD);
	}
	if (-1 != iClientFD)
	{	
		CloseSocket(iClientFD);
	}

	if (NULL != psSsl)
	{
		SSL_shutdown (psSsl); 
		SSL_free (psSsl);
	}
	if(NULL != psCtx)
	{
		SSL_CTX_free (psCtx);
	}
	return iRet;
}

